const DEFAULT_PARAMS = [14, 1]

function calcEma (dataList, period) {
  const emaList = []
  const smoothing = 2 / (period + 1)
  let prevEma = null
  dataList.forEach(data => {
    const price = data.close
    if (prevEma == null) {
      prevEma = price
    } else {
      prevEma = (price - prevEma) * smoothing + prevEma
    }
    emaList.push(prevEma)
  })
  return emaList
}

function calcStd (dataList, period) {
  const stdList = []
  let sum = 0
  let sumSquared = 0
  dataList.forEach((data, index) => {
    const price = data.close
    sum += price
    sumSquared += price * price
    if (index >= period) {
      const outPrice = dataList[index - period].close
      sum -= outPrice
      sumSquared -= outPrice * outPrice
    }
    if (index >= period - 1) {
      const mean = sum / period
      const variance = Math.max(sumSquared / period - mean * mean, 0)
      stdList.push(Math.sqrt(variance))
    } else {
      stdList.push(null)
    }
  })
  return stdList
}

const indicator = {
  name: 'BOLL_BREAKOUT',
  shortName: 'BBBO',
  calcParams: DEFAULT_PARAMS,
  figures: [
    { key: 'bull', title: 'Bull: ', type: 'line' },
    { key: 'bear', title: 'Bear: ', type: 'line' }
  ],
  calc: (dataList, indicatorInstance) => {
    const [length, multiplier] = indicatorInstance.calcParams ?? DEFAULT_PARAMS
    if (!dataList || dataList.length === 0) {
      return []
    }
    const emaList = calcEma(dataList, length)
    const stdList = calcStd(dataList, length)
    const results = []
    for (let i = 0; i < dataList.length; i++) {
      const ema = emaList[i]
      const std = stdList[i]
      const result = {}
      if (i >= length - 1 && std != null) {
        let bullSum = 0
        let bearSum = 0
        let bullDen = 0
        let bearDen = 0
        for (let j = i; j >= 0 && j > i - length; j--) {
          const price = dataList[j].close
          const emaJ = emaList[j]
          const stdJ = stdList[j]
          if (emaJ == null || stdJ == null) {
            continue
          }
          const upperJ = emaJ + stdJ * multiplier
          const lowerJ = emaJ - stdJ * multiplier
          const bullDiff = price - upperJ
          const bearDiff = lowerJ - price
          bullSum += Math.max(bullDiff, 0)
          bearSum += Math.max(bearDiff, 0)
          bullDen += Math.abs(bullDiff)
          bearDen += Math.abs(bearDiff)
        }
        const bullValue = bullDen === 0 ? 0 : (bullSum / bullDen) * 100
        const bearValue = bearDen === 0 ? 0 : (bearSum / bearDen) * 100
        result.bull = Number.isFinite(bullValue) ? bullValue : 0
        result.bear = Number.isFinite(bearValue) ? bearValue : 0
      } else {
        result.bull = null
        result.bear = null
      }
      results.push(result)
    }
    return results
  }
}

export default indicator
